//**********************************************************
// ProgDVB
//**********************************************************
ProgDvb = {};

function ProgDVB(_player, _playerName)
{
	MediaPlayer.call(this, _player, _playerName);

	var _this = this;
	var _mediaController = OSH.getCurrent().mediaController;

	var _osh = OSH.getCurrent();
	var _signalInfo = null;
	var _lastRecordingInfo = null;

	var _progDVB = getProgDVB();
	var _ignoreCommand;

	// Events
	this.onOSDShow = new Event();
	this.onOSDHide = new Event();
	this.onSubtitleUpdate = new Event();
	this.onSignalChange = new Event();

	this.onVolumeChange = new Event();
	this.onRecordStart = new Event();
	this.onRecordStop = new Event();
	this.onRecordProgress = new Event();

	var _pingTimer = new Timer("ProgDVB.pingTimer", 5000);

	var _signalRefreshInterval = 900;
	var _signalRefreshTimer = new Timer("ProgDVB.signalRefreshTimer", _signalRefreshInterval); // started by startSignalMonitor

	function initialize()
	{
		_pingTimer.setEnabled(true);
		_pingTimer.onTimeout.attach(this, onPingTimeout);
		
		_signalRefreshTimer.onTimeout.attach(this, onSignalRefreshTimeout);
	}

	function getProgDvbEx()
	{
		return _player;
	}
	
	function getProgDVB(checkItAlive)
	{
		if((_progDVB != null) && checkItAlive)
		{
			try
			{
				var version = _progDVB.Version; // ping ProgDVB
			}
			catch(e)
			{
				clearProgDVB();
				window.top.log.writeln("Connection with ProgDVB has been lost.");
			}
		}
		
		if(_progDVB == null)
		{
			try
			{
				_progDVB = GetObject("", "OSH.ProgDVB");
				eval("function _progDVB::OnOSDShow() { fireOnOSDShow(); }");
				eval("function _progDVB::OnOSDHide() { fireOnOSDHide(); }");
				//eval("function _progDVB::OnKey(key) { fireOnKey(key); }");
				//eval("function _progDVB::OnCommand(command) { fireOnCommand(command); }");
				eval("function _progDVB::OnSubtitleUpdate(subtitleInfo) { fireOnSubtitleUpdate(subtitleInfo); }");
				eval("function _progDVB::OnRecordStart(recordingInfo) { fireOnRecordStart(recordingInfo); }");
				eval("function _progDVB::OnRecordStop(recordingInfo) { fireOnRecordStop(recordingInfo); }");
				eval("function _progDVB::OnRecordProgress(recordingInfo) { fireOnRecordProgress(recordingInfo); }");
				
				window.top.log.writeln("Connection with ProgDVB has been established.");
			} catch(e) { clearProgDVB(); }
		}

		return _progDVB;
	}

	function clearProgDVB()
	{
		_progDVB = null;
	}

	this.getProgDvb = function()
	{
		return getProgDvbEx();
	}

	// #region MediaPlayer overrides	
	var _onStart = this._onStart;
	this._onStart = function(sender, args)
	{
		getProgDVB(true);
		return _onStart.call(this, sender, args);
	}
	
	var _onExit = this._onExit;
	this._onExit = function(sender, args)
	{
		clearProgDVB();
		return _onExit.call(this, sender, args);
	}

	var _onMediaChange = this._onMediaChange;
	this._onMediaChange = function(sender, args)
	{
		_this.getTimeline().stopVirtualSeeking(false);
		return _onMediaChange.call(this, sender, args);
	}
	// #endregion MediaPlayer overrides	
	

	this.getOSDVisible = function()
	{
		if(getProgDVB() == null)
			return false;
		
		try
		{
			return getProgDVB(true).OSDVisible;
		}
		catch(e)
		{
			this.processException(e);
			return false;
		}
	}

	this.showOSD = function()
	{
		if(getProgDVB() == null)
			return null;
		
		try
		{
			getProgDVB(true).ShowOSD();
		}
		catch(e) { this.processException(e); }
	}

	this.hideOSD = function()
	{
		if(getProgDVB() == null)
			return null;
		
		try
		{
			getProgDVB(true).HideOSD();
		}
		catch(e) { this.processException(e); }
	}

	this.getSignalInfo = function()
	{
		updateSignalInfo();
		return _signalInfo;
	}

	this.getSignalLevel = function()
	{
		updateSignalInfo();
		return _signalInfo.level;
	}

	this.getSignalQuality = function()
	{
		updateSignalInfo();
		return _signalInfo.quality;
	}

	function updateSignalInfo()
	{
		if(_signalInfo && (Date.utcNow().valueOf() < _signalInfo.updatedUtc.valueOf() + _signalRefreshInterval - 100))
			return;

		var newSignalInfo = { updatedUtc: Date.utcNow(), level: 0, quality: 0 };
		try
		{
			if(getProgDVB(true) != null)
			{	
				newSignalInfo.level = getProgDVB(false).SignalLevel;
				newSignalInfo.quality = getProgDVB(false).SignalQuality;
			}
		}
		catch(e)
		{
			_this.processException(e);
		}

		var hasChanged = !_signalInfo ||
			_signalInfo.level != newSignalInfo.level ||
			_signalInfo.quality != newSignalInfo.quality;

		_signalInfo = newSignalInfo;

		if(hasChanged)
			fireOnSignalChange(newSignalInfo);
	}

	function onSignalRefreshTimeout(sender, event)
	{
		if(_this.onSignalChange.hasHandlers())
			updateSignalInfo();	
	}

	this.startSignalMonitor = function()
	{
		_signalRefreshTimer.start();
	}
	
	this.stopSignalMonitor = function()
	{
		_signalRefreshTimer.stop();
	}
	
	this.getVolume = function()
	{
		if(getProgDVB() == null)
			return 0;

		try
		{
			return getProgDVB(true).Volume;
		}
		catch(e)
		{
			this.processException(e);
			return 0;
		}
	}

	this.setVolume = function(value)
	{
		if(getProgDVB() == null)
			return;

		try
		{
			var newValue = parseInt(value);
			if(getProgDVB(true).Volume == newValue || isNaN(newValue))
				return;
			
			getProgDVB().Volume = value;
			fireOnVolumeChange(getProgDVB().Volume);
		}
		catch(e) { this.processException(e); }
	}

	this.setChannel = function(channel)
	{
		if(channel == null)
			this.sendCommand("CH");
		else if(Channel.isInstanceOfType(channel))
			this.sendCommand("CH@" + channel.channelNumber);
		else if(channel.length > 10)
		{
			if(!getProgDVB()) return;
			try
			{
				getProgDVB(true).SetChannel(channel);
			}
			catch(e) { this.processException(e); }
		}
		else
			this.sendCommand("CH@" + channel);
	}

	this.startRecord = function()
	{
		this.sendCommand("StartRecord");
	}

	this.stopRecord = function()
	{
		this.sendCommand("StopRecord");
	}

	this.addSchedulerRecord = function(dateTime, command, parameters)
	{
		if(!parameters)
			parameters = "";
		
		window.top.log.writeln("Adding ProgDVB scheduler record: " + dateTime + "-" + command + "-" + parameters);

		if(getProgDVB() == null)
			return;

		try
		{
			getProgDVB(true).AddSchedulerRecord(dateTime.getVarDate(), command, parameters);
		}
		catch(e) { this.processException(e); }
	}

	this.sendCommand = function(command)
	{
		window.top.log.writeln("Sending ProgDVB command: " + command);
		
		if(getProgDVB() == null)
			return;

		try
		{
			getProgDVB(true).SendCommand(command);
			_ignoreCommand = command;
		}
		catch(e) { this.processException(e); }
	}

	var _processCommand = this.processCommand;
	this.processCommand = function(sender, args)
	{
		var command = args.command;
		var commandLowerCase = command.toLowerCase();
		if(commandLowerCase.substr(0, 8) == "forward:" && commandLowerCase.length > 8)
		{
			if(args.source == this)
			{
				window.top.log.writeln("ProgDVB command forwarding canceled.");
			}
			else
			{
				command = command.substr(8);
				if(doesCommandRequireNoOSD(command))
				{
					var visible = this.getOSDVisible();
					if(visible)
						this.hideOSD();
				}
				this.sendCommand(command);
			}
			args.processed = true;
			return true;
		}
		else
			return _processCommand.call(this, sender, args);
	}
	
	function doesCommandRequireNoOSD(command)
	{
		command = command.toLowerCase();
		return (command == "left" || command == "right" || command == "up" || command == "down");
	}

	this.getLastRecordingInfo = function()
	{
		return _lastRecordingInfo;
	}

	this.getRecordingInfo = function()
	{
		if(getProgDVB() == null)
			return null;

		try
		{
			return getRecordingInfo(getProgDVB(true).RecordingInfo);
		}
		catch(e)
		{
			this.processException(e);
			return null;
		}
	}

	function getRecordingInfo(recordingInfo)
	{
		try
		{
			if(recordingInfo == null)
				return null;

			recordingInfo = new ProgDvb.RecordingInfo(recordingInfo);
			_lastRecordingInfo = recordingInfo;
			
			return recordingInfo;
		}
		catch(e)
		{
			_this.processException(e);
			return null;
		}
	}

	function onPingTimeout(sender, event)
	{
		try
		{
			_this.getProgDVB(true);
		}
		catch(e) {}
	}

	this.processException = function(e)
	{
		window.top.log.writeln("ProgDVB exception: " + e.number + " (" + e.description + ")");
		
		// -2147417851 (0x80010105) - The server threw an exception. 
		// -2146827826 (0x800A01CE) - The remote server machine does not exist or is unavailable
		// -2147023898 (0x800703E6) - Invalid access to memory location. 
		// -2146823281 (0x800A138F) - 'name' is null or not an object
		// -2147023170 (0x800706BE) - The remote procedure call failed.
		if(e.number == -2147417851 || e.number == -2146827826 ||
			e.number == -2147023898 || e.number == -2146823281 ||
			e.number == -2147023170)
		{
			clearProgDVB();
		}
		else
		{
			throw e;
		}
	}	

	//
	// Private Methods
	//

	function fireOnOSDShow()
	{
		window.top.log.writeln("ProgDVB OSD show");

		var e = window.top.document.createEventObject();
		_this.onOSDShow.fire(_this, e);
	}

	function fireOnOSDHide()
	{
		window.top.log.writeln("ProgDVB OSD hide");

		var e = window.top.document.createEventObject();
		_this.onOSDHide.fire(_this, e);
	}

	function fireOnKey(key)
	{
		window.top.log.writeln("ProgDVB key: " + key);

		_osh.processKey(key, _this);
	}

	/// <summary>
	/// Processes commands received from ProgDVB (command line, scheduler, WM_SETTEXT)
	/// forwarding them to OSH.
	/// </summary>
	function fireOnCommand(command)
	{
		window.top.log.writeln("ProgDVB command: " + command);

		if(command.substr(0, 1) == "/")
		{
			command = command.substr(1);
			var colonPos = command.indexOf(":");
			if(colonPos > 0)
				command = command.substr(0, colonPos) + "@" + command.substr(colonPos + 1);
		}

		var ignoreCommand = _ignoreCommand;
		_ignoreCommand = null;
		
		if(ignoreCommand && command == ignoreCommand)
		{
			window.top.log.writeln("ProgDVB command was ignored.");
			return;
		}

		var processed = false;
		if(!_this.getOSDVisible())
		{
			var key = translateCommandToKey(command);
			if(key)
				processed = _osh.processKey(key, _this);
		}

		if(!processed)
			window.top.log.writeln("ProgDVB command was not processed.");
			
		if(!processed)
		{
			var commandLowerCase = command.toLowerCase();
			if(commandLowerCase == "volumeup" || commandLowerCase == "volumedown")
			{
				fireOnVolumeChange(_this.getVolume());
			}
		}
	}
	
	function translateCommandToKey(command)
	{
		command = command.toLowerCase();
		var key;
		if(command.length == 1)
		{
			key = command.charCodeAt(0);
			if(key < Keys.VK_0 || key > Keys.VK_9)
				key = null;
		}
		else if(command == "left") key = Keys.VK_LEFT;
		else if(command == "right") key = Keys.VK_RIGHT;
		else if(command == "up") key = Keys.VK_UP;
		else if(command == "down") key = Keys.VK_DOWN;
		else if(command == "showosdmainmenu") key = Keys.VK_START;
		else if(command == "showosdinfo") key = Keys.VK_INFO;
		else if(command == "ch") key = Keys.VK_ENTER;
		else if(command == "playbackfirst") key = Keys.VK_PREVTRACK;
		else if(command == "playbacklast") key = Keys.VK_NEXTTRACK;
		else if(command == "playbackprev") key = Keys.VK_REWIND;
		else if(command == "playbacknext") key = Keys.VK_FASTFORWARD;
		else if(command == "play") key = Keys.VK_PLAY;
		else if(command == "pause") key = Keys.VK_PAUSE;
		else if(command == "stop") key = Keys.VK_STOP;
		else if(command == "recordswitch") key = Keys.VK_RECORD;
		
		return key;
	}

	function fireOnSubtitleUpdate(subtitleInfo)
	{
		var e = window.top.document.createEventObject();
		e.subtitleInfo = subtitleInfo;
		_this.onSubtitleUpdate.fire(_this, e);
	}
	
	function fireOnVolumeChange(volume)
	{
		var e = window.top.document.createEventObject();
		e.volume = volume;
		_this.onVolumeChange.fire(_this, e);
	}

	function fireOnRecordStart(recordingInfo)
	{
		window.top.log.writeln("ProgDVB.OnRecordStart()");

		var e = window.top.document.createEventObject();
		e.recordingInfo = getRecordingInfo(recordingInfo);

		var totalTime = e.recordingInfo.getTotalTime();
		var totalSeconds = (totalTime.getHours() * 60 + totalTime.getMinutes()) * 60 + totalTime.getSeconds();
		_this.getTimeline().onRecordStart(totalSeconds);

		recordingInfo = null;
		_this.onRecordStart.fire(_this, e);
	}

	function fireOnRecordStop(recordingInfo)
	{
		window.top.log.writeln("ProgDVB.OnRecordStop()");
		
		var e = window.top.document.createEventObject();
		e.recordingInfo = getRecordingInfo(recordingInfo);

		_this.getTimeline().onRecordStop();

		recordingInfo = null;
		_this.onRecordStop.fire(_this, e);
	}

	function fireOnRecordProgress(recordingInfo)
	{
		var e = window.top.document.createEventObject();
		e.recordingInfo = getRecordingInfo(recordingInfo);
		recordingInfo = null;
		_this.onRecordProgress.fire(_this, e);
	}

	function fireOnSignalChange(signalInfo)
	{
		var e = window.top.document.createEventObject();
		e.signalInfo = signalInfo;
		_this.onSignalChange.fire(_this, e);
	}
	
	initialize();
}
Type.registerType('ProgDVB', MediaPlayer);


//**********************************************************
// RecordingInfo
//**********************************************************
ProgDvb.RecordingInfo = function(recordingInfo)
{
	this._totalTime = new Date(recordingInfo.TotalTime);
	this._totalSize = recordingInfo.TotalSize;
	this._freeSize = recordingInfo.FreeSize;
	var files = new VBArray(recordingInfo.Files).toArray();
	this._filePath = files.length > 0 ? files[0] : null;
	recordingInfo = null;
}

ProgDvb.RecordingInfo.prototype.getTotalTime = function()
{
	return this._totalTime;
}

ProgDvb.RecordingInfo.prototype.getTotalSize = function()
{
	return this._totalSize;
}

ProgDvb.RecordingInfo.prototype.getFreeSize = function()
{
	return this._freeSize;
}

ProgDvb.RecordingInfo.prototype.getFilePath = function()
{
	return this._filePath;
}
Type.registerType('ProgDvb.RecordingInfo');

MediaController.registerPlayerType("Osh.ProgDvb.ProgDvbApplication", ProgDVB);
